#ifndef _CAD_SLICER_GEOMETRY_H_
#define _CAD_SLICER_GEOMETRY_H_

#include <set>
#include <array>
#include <vector>
#include <utility>
#include <array>
#include <complex>
#include <algorithm>

#include <prenstar/assert.h>


namespace cadslicer
{

typedef std::complex<float> point2f;
typedef std::pair<point2f, point2f> edge;
typedef std::vector<point2f> contour;
typedef std::vector<contour> contours;
typedef std::array<float, 4> bounding_box;
typedef std::vector<bounding_box> bounding_boxes;

typedef void const* mesh;
typedef void const* face;
typedef std::pair<mesh, face> face_id;
typedef std::set<face_id> faces;

typedef void const* vertex_id;
struct sort_tag {};

struct edge_id
{
  std::array<vertex_id,2> ids;

  edge_id(vertex_id p1, vertex_id p2)
    : ids{{p1,p2}} {}

  edge_id(vertex_id p1, vertex_id p2, sort_tag)
    : ids{{p1,p2}} { sort(); }

  void sort(void)
  {
    prenstar_assert_ne(ids[0], ids[1]);

    (ids[0] > ids[1])
        ? std::swap(ids[0], ids[1])
        : (void) nullptr;

    prenstar_assert_lt(ids[0], ids[1]);
  }

  /// @brief Lexicographic order on vertice pointers.
  bool operator <(edge_id const& ed) const
  {
    return (ids[0] != ed.ids[0])
        ? (ids[0] < ed.ids[0])
        : (ids[1] < ed.ids[1]);
  }

  /// @brief Comparison for sorted edge_id.
  /// @warning The two compared edge_is have to be sorted.
  bool operator ==(edge_id const& ed) const
  { return ids[0] == ed.ids[0] && ids[1] == ed.ids[1]; }

  bool operator !=(edge_id const& ed) const
  { return ids[0] != ed.ids[0] || ids[1] != ed.ids[1]; }
};

struct triangle_id
{
  std::array<vertex_id,3> ids;

  triangle_id(vertex_id p1, vertex_id p2, vertex_id p3)
    : ids{{p1,p2,p3}} {}

  triangle_id(vertex_id p1, vertex_id p2, vertex_id p3, sort_tag)
    : ids{{p1,p2,p3}} { sort(); }

  void sort(void)
  {
    prenstar_assert_ne(ids[0], ids[1]);
    prenstar_assert_ne(ids[0], ids[2]);
    prenstar_assert_ne(ids[2], ids[1]);

    /**/ if (ids[0] > ids[1])
    {
      /**/ if (ids[1] > ids[2]) std::swap(ids[0], ids[2]);
      else if (ids[0] < ids[2]) std::swap(ids[1], ids[0]);
      else
      {
        auto a = ids[0];
        ids[0] = ids[1];
        ids[1] = ids[2];
        ids[2] = a;
      }
    }
    else if (ids[1] > ids[2])
    {
      if (ids[0] < ids[2]) std::swap(ids[1], ids[2]);
      else
      {
        auto c = ids[2];
        ids[2] = ids[1];
        ids[1] = ids[0];
        ids[0] = c;
      }
    }

    prenstar_assert_lt(ids[0], ids[1]);
    prenstar_assert_lt(ids[1], ids[2]);
  }

  /// @brief Lexicographic order on vertice pointers.
  bool operator <(triangle_id const& triangle) const
  {
    return (ids[0] != triangle.ids[0])
        ? (ids[0] < triangle.ids[0])
        : (ids[1] != triangle.ids[1])
          ? (ids[1] < triangle.ids[1])
          : (ids[2] < triangle.ids[2]);
  }
};

struct triangle_intersection
{
  triangle_intersection(point2f&& p1, point2f&& p2, triangle_id&& t)
    : intersection(std::make_pair(std::forward<point2f>(p1), std::forward<point2f>(p2)))
    , vertice(std::forward<triangle_id>(t))
    , first(vertice.ids[0], vertice.ids[1], sort_tag())
    , second(vertice.ids[0], vertice.ids[2], sort_tag()) {}

  /// @brief The intersection between the plane of the current slice
  ///        and the triangle.
  edge intersection;

  /// @brief The vertice of the triangle.
  ///
  /// intersection.first is the intersection of the plane with
  /// vertice[0] and vertice[1].
  /// intersection.second is the intersection of the plane with
  /// vertice[0] and vertice[2].
  triangle_id vertice;

  edge_id first, second;
};

struct triangle_neighbour
{
  triangle_intersection* first = nullptr;
  triangle_intersection* second = nullptr;
};

} // cadslicer

#endif // _CAD_SLICER_GEOMETRY_H_
